namespace SimpleWebServices.Data
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Net;
    using System.Web;
    using System.Xml;
    using System.Xml.Serialization;
    using SimpleWebServices;
    using SimpleWebServices.Security.Cryptography;
    using SimpleWebServices.Web;
    using Xunit;

    public sealed class DataCollectionFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<DataCollection>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Implements<IEnumerable<KeyValuePair<string, string>>>()
                .Implements<IXmlSerializable>()
                .Implements<IRecord>()
                .XmlRoot("data")
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            DataCollection obj = "<data completed='true'><value name='name1'>value1</value><value name='name2'>value2</value></data>".Deserialize<DataCollection>();

            Assert.True(obj.Completed);
            Assert.True(obj.Contains("name1"));
            Assert.True(obj.Contains("name2"));
        }

        [Fact]
        public void deserializeEmpty()
        {
            Assert.Equal<int>(0, "<data />".Deserialize<DataCollection>().Count);
        }

        [Fact]
        public void serialize()
        {
            DataCollection obj = new DataCollection();
            obj.Completed = true;
            obj.Add("foo", "bar");

            Assert.True((bool)obj.Serialize().CreateNavigator().Evaluate("1=count(/data[@completed='true']/value[@name='foo'][text()='bar'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.Equal<int>(0, new DataCollection().Count);
        }

        [Fact]
        public void prop_Cacheability()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Cacheability"))
                .IsAutoProperty<HttpCacheability?>()
                .Result);
        }

        [Fact]
        public void prop_Created()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Created"))
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Entity()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Entity"))
                .TypeIs<string>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void prop_Entity_get()
        {
            DataCollection obj = (
                "<data completed='true'>" +
                    "<value name='name1'>value1</value>" +
                    "<value name='name2'>value2</value>" +
                "</data>").Deserialize<DataCollection>();

            string expected = "WbDR0ADqWI3NVuj+/oqyrA==";
            string actual = MD5Hash.Compute(obj.Entity);

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void prop_Entity_getEmpty()
        {
            string expected = string.Empty;
            string actual = new DataCollection().Entity;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void prop_Etag()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Etag"))
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Expires()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Expires"))
                .IsAutoProperty<Expiration>()
                .Result);
        }

        [Fact]
        public void prop_Modified()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Modified"))
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Status()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Status"))
                .IsAutoProperty<HttpStatusCode?>()
                .Result);
        }

        [Fact]
        public void prop_Token()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Token"))
                .IsAutoProperty<Token?>()
                .Result);
        }

        [Fact]
        public void prop_Urn()
        {
            Assert.True(new PropertyExpectations(typeof(DataCollection).GetProperty("Urn"))
                .IsAutoProperty<AbsoluteUri>()
                .Result);
        }

        [Fact]
        public void indexer_int_get()
        {
            DataCollection obj = new DataCollection();
            obj.Add("one", "1");
            obj.Add("two", "2");
            obj.Add("three", "3");

            Assert.Equal<string>("two", obj[1].Key);
            Assert.Equal<string>("2", obj[1].Value);
        }

        [Fact]
        public void indexer_int_get_whenNotFound()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new DataCollection()[99]);
        }

        [Fact]
        public void indexer_int_set()
        {
            DataCollection obj = new DataCollection();
            obj.Add("one", "1");
            obj.Add("nine", "9");

            obj[1] = new KeyValuePair<string, string>("two", "2");

            Assert.Equal<string>("two", obj[1].Key);
            Assert.Equal<string>("2", obj[1].Value);
        }

        [Fact]
        public void indexer_int_set_whenAdd()
        {
            DataCollection obj = new DataCollection();
            obj.Add("one", "1");

            Assert.Throws<ArgumentOutOfRangeException>(() => obj[1] = new KeyValuePair<string, string>("two", "2"));
        }

        [Fact]
        public void indexer_string_get()
        {
            string expected = "2";

            DataCollection obj = new DataCollection();
            obj.Add("one", "1");
            obj.Add("two", expected);
            obj.Add("three", "3");

            string actual = obj["two"];

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void indexer_string_get_whenMultiple()
        {
            string expected = "1,2,3";

            DataCollection obj = new DataCollection();
            obj.Add("name", "1");
            obj.Add("name", "2");
            obj.Add("name", "3");

            string actual = obj["name"];

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void indexer_string_get_whenNotFound()
        {
            Assert.Null(new DataCollection()["unknown"]);
        }

        [Fact]
        public void indexer_string_set()
        {
            DataCollection expected = new DataCollection();
            expected.Add("one", "1");
            expected.Add("two", "2");

            DataCollection actual = new DataCollection();
            actual.Add("one", "1");
            actual.Add("two", string.Empty);
            actual["two"] = "2";

            Assert.Equal<DataCollection>(expected, actual);
        }

        [Fact]
        public void indexer_string_set_whenMultiple()
        {
            DataCollection obj = new DataCollection();
            obj["name"] = "1,2,3";

            Assert.Equal<string>("name", obj[0].Key);
            Assert.Equal<string>("name", obj[1].Key);
            Assert.Equal<string>("name", obj[2].Key);

            Assert.Equal<string>("1", obj[0].Value);
            Assert.Equal<string>("2", obj[1].Value);
            Assert.Equal<string>("3", obj[2].Value);
        }

        [Fact]
        public void indexer_string_set_whenMultipleReplace()
        {
            DataCollection obj = new DataCollection();
            obj["name"] = "1,2,3";
            obj["name"] = "4,5,6";

            Assert.Equal<string>("name", obj[0].Key);
            Assert.Equal<string>("name", obj[1].Key);
            Assert.Equal<string>("name", obj[2].Key);

            Assert.Equal<string>("4", obj[0].Value);
            Assert.Equal<string>("5", obj[1].Value);
            Assert.Equal<string>("6", obj[2].Value);
        }

        [Fact]
        public void indexer_string_set_whenAdd()
        {
            DataCollection expected = new DataCollection();
            expected.Add("one", "1");
            expected.Add("two", "2");

            DataCollection actual = new DataCollection();
            actual.Add("one", "1");
            actual["two"] = "2";

            Assert.Equal<DataCollection>(expected, actual);
        }

        [Fact]
        public void opEquality_DataCollection_DataCollection_TrueTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = new DataCollection();

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opEquality_DataCollection_DataCollection_FalseTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = new DataCollection();
            operand2.Add("name", "value");

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opEquality_DataCollection_DataCollection_SameTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = operand1;

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opEquality_DataCollectionNull_DataCollection()
        {
            DataCollection operand1 = null;
            DataCollection operand2 = new DataCollection();

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opEquality_DataCollection_DataCollectionNull()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = null;

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opInequality_DataCollection_DataCollection_TrueTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = new DataCollection();
            operand2.Add("name", "value");

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opInequality_DataCollection_DataCollection_FalseTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = new DataCollection();

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_DataCollection_DataCollection_SameTest()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = operand1;

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_DataCollectionNull_DataCollection()
        {
            DataCollection operand1 = null;
            DataCollection operand2 = new DataCollection();

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opInequality_DataCollection_DataCollectionNull()
        {
            DataCollection operand1 = new DataCollection();
            DataCollection operand2 = null;

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void op_FromPostData_NameValueCollectionNull()
        {
            Assert.Throws<ArgumentNullException>(() => DataCollection.FromPostData(null as NameValueCollection));
        }

        [Fact]
        public void op_FromPostData_NameValueCollectionEmpty()
        {
            Assert.Empty(DataCollection.FromPostData(new NameValueCollection()));
        }

        [Fact]
        public void op_FromPostData_NameValueCollection()
        {
            NameValueCollection form = new NameValueCollection
            {
                { "foo", "bar" },
                { "checkbox", "first,second" }
            };

            DataCollection obj = DataCollection.FromPostData(form);

            Assert.Equal<string>("bar", obj["foo"]);
            Assert.Equal<string>("checkbox", obj[1].Key);
            Assert.Equal<string>("first", obj[1].Value);
            Assert.Equal<string>("checkbox", obj[2].Key);
            Assert.Equal<string>("second", obj[2].Value);
        }

        [Fact]
        public void op_Add_DataCollection()
        {
            DataCollection expected = new DataCollection
            {
                new KeyValuePair<string, string>("name1", "value1"),
                new KeyValuePair<string, string>("name2", "value2")
            };

            DataCollection actual = new DataCollection();
            actual.Add(expected);

            Assert.Equal<DataCollection>(expected, actual);
        }

        [Fact]
        public void op_Add_DataCollectionEmpty()
        {
            DataCollection expected = new DataCollection();
            DataCollection actual = new DataCollection();
            actual.Add(new DataCollection());

            Assert.Equal<DataCollection>(expected, actual);
        }

        [Fact]
        public void op_Add_DataCollectionNull()
        {
            Assert.Throws<ArgumentNullException>(() => new DataCollection().Add(null as DataCollection));
        }

        [Fact]
        public void op_Add_string_string()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", "value");

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void op_Add_stringEmpty_string()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new DataCollection().Add(string.Empty, "value"));
        }

        [Fact]
        public void op_Add_stringNull_string()
        {
            Assert.Throws<ArgumentNullException>(() => new DataCollection().Add(null, "value"));
        }

        [Fact]
        public void op_Add_string_stringEmpty()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", string.Empty);

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void op_Add_string_stringNull()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", null);

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void op_Add_string_stringMultiple()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", "1,2,3");

            Assert.Equal<string>("name", obj[0].Key);
            Assert.Equal<string>("name", obj[1].Key);
            Assert.Equal<string>("name", obj[2].Key);

            Assert.Equal<string>("1", obj[0].Value);
            Assert.Equal<string>("2", obj[1].Value);
            Assert.Equal<string>("3", obj[2].Value);

            obj.Add("name", "4,5,6");

            Assert.Equal<string>("name", obj[3].Key);
            Assert.Equal<string>("name", obj[4].Key);
            Assert.Equal<string>("name", obj[5].Key);

            Assert.Equal<string>("4", obj[3].Value);
            Assert.Equal<string>("5", obj[4].Value);
            Assert.Equal<string>("6", obj[5].Value);
        }

        [Fact]
        public void op_Contains_string()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", "value");

            Assert.True(obj.Contains("name"));
        }

        [Fact]
        public void op_Contains_stringMissing()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", "value");

            Assert.False(obj.Contains("???"));
        }

        [Fact]
        public void op_Contains_stringEmpty()
        {
            Assert.False(new DataCollection().Contains(string.Empty));
        }

        [Fact]
        public void op_Contains_stringNull()
        {
            Assert.False(new DataCollection().Contains(null));
        }

        [Fact]
        public void op_Contains_string_string()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", string.Empty);
            obj.Add("name", "value");

            Assert.True(obj.Contains("name", "value"));
        }

        [Fact]
        public void op_Contains_string_stringMissing()
        {
            DataCollection obj = new DataCollection();

            obj.Add("name", "value");

            Assert.False(obj.Contains("name", "???"));
        }

        [Fact]
        public void op_Equals_object()
        {
            DataCollection obj = new DataCollection();
            obj.Add("name", "value");

            DataCollection comparand = new DataCollection();
            comparand.Add("name", "value");

            Assert.True(obj.Equals(comparand as object));
        }

        [Fact]
        public void op_Equals_objectDiffer()
        {
            DataCollection obj = new DataCollection();
            obj.Add("name", "value");

            DataCollection comparand = new DataCollection();
            comparand.Add("foo", "bar");

            Assert.False(obj.Equals(comparand as object));
        }

        [Fact]
        public void op_Equals_objectSame()
        {
            DataCollection obj = new DataCollection();

            Assert.True(obj.Equals(obj as object));
        }

        [Fact]
        public void op_Equals_objectNull()
        {
            Assert.False(new DataCollection().Equals(null as object));
        }

        [Fact]
        public void op_Equals_objectString()
        {
            Assert.False(new DataCollection().Equals("test" as object));
        }

        [Fact]
        public void op_GetHashCode()
        {
            DataCollection obj = new DataCollection();

            int expected = obj.ToString().GetHashCode();
            int actual = obj.GetHashCode();

            Assert.Equal<int>(expected, actual);
        }

        [Fact]
        public void op_ToString()
        {
            DataCollection obj = new DataCollection();
            obj.Add("name1", "value1");
            obj.Add("name2", "value2");

            string expected = obj.Serialize().CreateNavigator().OuterXml;
            string actual = obj.ToString();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void IEnumerable_op_GetEnumerator()
        {
            Assert.IsAssignableFrom<IEnumerator>((new DataCollection() as IEnumerable).GetEnumerator());
        }

        [Fact]
        public void IEnumerableOfT_op_GetEnumerator()
        {
            Assert.IsAssignableFrom<IEnumerator<KeyValuePair<string, string>>>((new DataCollection() as IEnumerable<KeyValuePair<string, string>>).GetEnumerator());
        }

        [Fact]
        public void IXmlSerializable_op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new DataCollection() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void IXmlSerializable_op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new DataCollection() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void IXmlSerializable_op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new DataCollection() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}